package org.jflame.captcha.web.spring;

import org.jflame.captcha.Captcha;
import org.jflame.captcha.CaptchaService;
import org.jflame.captcha.cache.CaptchaCache;
import org.jflame.captcha.cache.CaptchaCaffeineCache;
import org.jflame.captcha.creator.SimpleImage;
import org.jflame.captcha.creator.factory.CaptchaCreatorFactory;
import org.jflame.captcha.web.CaptchaSessionCache;
import org.jflame.commons.file.ImageHelper;
import org.jflame.commons.key.IDHelper;
import org.jflame.commons.model.CallResult;
import org.jflame.commons.model.CallResult.ResultEnum;
import org.jflame.context.spring.SpringPropertiesConfig;

import java.io.IOException;
import java.util.function.Supplier;

import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Controller
@SuppressWarnings("rawtypes")
public class CaptchaController {

    @Autowired
    private Environment env;

    @Autowired(required = false)
    private CaptchaCache captchaCache;

    private CaptchaService captchaService;

    @PostConstruct
    public void initCfg() {
        SpringPropertiesConfig captchaConfig = new SpringPropertiesConfig(env);
        if (captchaCache == null) {
            String cacheType = captchaConfig.getString(CaptchaCreatorFactory.KEY_CACHE_TYPE);
            if ("session".equals(cacheType)) {
                captchaCache = new CaptchaSessionCache(new Supplier<HttpServletRequest>() {

                    @Override
                    public HttpServletRequest get() {
                        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder
                                .getRequestAttributes();
                        if (sra != null) {
                            return sra.getRequest();
                        }
                        return null;
                    }
                });
            } else if ("caffieine".equals(cacheType)) {
                captchaCache = new CaptchaCaffeineCache(1000, 300);
            } else {
                throw new RuntimeException("未注入captchaCache");
            }
        }
        captchaService = new CaptchaService(captchaConfig, captchaCache);
    }

    /**
     * 生成验证码
     * 
     * @param id 验证码id
     * @param captchaType 验证码类型,默认传统文字图片
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping("/captcha")
    public void generate(@RequestParam(defaultValue = "valid_code") String id,
            @RequestParam(defaultValue = "simple_image") String captchaType, HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        Captcha captcha = captchaService.create(captchaType, id);
        if (!(captcha.getDisplay() instanceof SimpleImage)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "不支持的验证码类型");
            return;
        }
        ServletOutputStream sos = response.getOutputStream();
        SimpleImage display = (SimpleImage) captcha.getDisplay();
        if (display.isOutJson()) {
            response.setContentType("application/json");
        } else {
            response.setContentType(ImageHelper.getContentType(display.getImageFormat()));
        }
        display.write(sos);
    }

    /**
     * 检验验证码
     * 
     * @param id
     * @param captchaType 验证码类型,默认传统文字图片
     * @param captcha 用户输入验证码,具体格式看对应验证码创建器
     * @param request
     * @return
     */
    @PostMapping("/captcha/verify")
    public CallResult<String> verify(@RequestParam(defaultValue = "valid_code") String id,
            @RequestParam(defaultValue = "simple_image") String captchaType,
            @RequestParam(required = true) String captcha, HttpServletRequest request) {
        CallResult<String> result = new CallResult<>(ResultEnum.PARAM_ERROR.getStatus(), "验证码错误");
        boolean isOk = captchaService.verify(id, captchaService.parse(captchaType, captcha));
        if (isOk) {
            String cr = id + IDHelper.millisAndRandomNo(3);
            request.getSession()
                    .setAttribute(id + "_checkresult", cr);
            result.setData(cr);
        }
        return result;
    }

}
